import bpy
from os import listdir
from os.path import join, exists, splitext
from typing import List
from bpy.types import Context
from bpy.types import PropertyGroup, Material
from bpy.props import EnumProperty, BoolProperty, PointerProperty
from ...addon.paths import FluidLabPaths
from ...libs.materials.db_control import build_in_mat_lib, material_types
# from bpy.utils import previews
from ...libs.t3dn_bip import previews
from ...libs.functions.get_common_vars import get_common_vars
from ...libs.functions.nodes import get_node_index_or_identifier_by


class FluidLab_PG_Shading(PropertyGroup):
    
    """ context.scene.fluidlab.shader_props.x """

    shader_subsectioins: EnumProperty(
        items=[
            ('ASSING',  "Assing",   "Assing materials", 0),
            ('MAT_SETTINGS',   "Settings",    "mat_settings materials",  1)
        ],
        default='ASSING',
    )

    def material_types_update(self, context):
        fluidlab = get_common_vars(context, get_fluidlab=True)
        fluidlab.shader_props.get_thumbnails_items(context, rescan=True)

    material_types: EnumProperty(
        items=[
            ('FLUID',   "Fluid",        "Fluid Materials",      0),
            ('GRAIN',   "Particles",    "Particles Materials",  1),
            ('CUSTOM',  "Custom",       "Custom Materials", 2),
        ],
        default='FLUID',
        update=material_types_update
    )
    
    # para el listado de custom materials:
    material_pointer: PointerProperty(name="Custom Material", type=Material, description="Select a material from the scene")

    thumbs_coll = {}
    enum_items = []

    def is_valid_mat(self, context, name:str) -> bool:
        """ compruebo si es un material contemplado por nosotros en el whitelist build_in_mat_lib """
        materials = [data for data in build_in_mat_lib if data[0] == "mat"]
        return next((True for data in materials if data[1] == name), False)            
        

    def get_thumbnails_items(self, context: Context, rescan: bool) -> List[tuple]:

        if not rescan:
            return self.enum_items
        else:
            
            # Si estamos en modo rescan y custom:
            if self.material_types == 'CUSTOM':
                # Si no hay thumnails previas:
                if len(self.enum_items) == 0:
                    # estipulo fluid mismamente para poder recuperarlas:
                    self.material_types = 'FLUID'
                else:
                    # Si ya había las devolvemos:
                    return self.enum_items

            # print("Rescan solicitado, limpiando colecciones de vistas previas existentes...")

            pcoll = self.thumbs_coll.get("shading")
            if pcoll:
                # print("Eliminando colección de vista previa existente: shading")
                bpy.utils.previews.remove(pcoll)
                self.thumbs_coll.pop("shading", None)
                self.enum_items.clear()

            # Crear una nueva colección de vistas previas
            # self.thumbs_coll["shading"] = previews.new()
            self.thumbs_coll["shading"] = previews.new(max_size=(512, 512)) # max_size es por el t3dn_bip.preview (el de blender no tiene eso)
            preview_coll = self.thumbs_coll["shading"]

            directory = FluidLabPaths.LIBS_MAT_THUMBS_PATH
            # print(f"Scanning directory: {directory}")

            if directory and exists(directory):
                filenames = [fname for fname in listdir(directory) if fname.lower().endswith(".bip") and material_types[fname.replace(".bip", "")] == self.material_types]
                # print(f"Files found: {filenames}")

                for i, name in enumerate(filenames):
                    filepath = join(directory, name)
                    thumb = preview_coll.load(name, filepath, 'IMAGE')
                    name_clean, extension = splitext(name)

                    # Descartamos si no está contenplado el material en nuestro build_in_mat_lib:
                    if not self.is_valid_mat(context, name_clean):
                        continue

                    self.enum_items.append((name, name_clean, "Shader", thumb.icon_id, i))
                    self.thumbnails = name
    
                return self.enum_items

    thumbnails: EnumProperty(items=lambda self, context: self.get_thumbnails_items(context, rescan=False))


    # shading props:
    def show_volume_update(self, context) -> None:

        fluid_mesh = get_common_vars(context, get_fluid_mesh=True)
        
        active_mesh_item = fluid_mesh.active
        active_mesh = active_mesh_item.ob
        if not active_mesh:
            return
        
        active_mat = active_mesh.active_material
        if not active_mat:
            return
        
        node_tree = active_mat.node_tree 
        node = next((node for node in node_tree.nodes if node.name.startswith("switch")), None)
        if not node:
            return
        
        identifier = get_node_index_or_identifier_by("index", "name", "inputs", node, "Fac", debug=False)
        if identifier is None:
            return        
        
        node.inputs[identifier].default_value = self.show_volume

    show_volume: BoolProperty(
         name = "Show Volume",
        description = "Show Internal Volume",
        default=False,
        update=show_volume_update
    )
    
    @staticmethod
    def update_node_property(self, context, node_name_prefix:str, property_name:str, property_values:dict, call_from:str) -> None:

        fluid_mesh = get_common_vars(context, get_fluid_mesh=True)
        
        active_mesh_item = fluid_mesh.active
        active_mesh = active_mesh_item.ob
        if not active_mesh:
            return
        
        active_mat = active_mesh.active_material
        if not active_mat:
            return
        
        node_tree = active_mat.node_tree 
        if not node_tree:
            return
        
        node = next((node for node in node_tree.nodes if node.name.startswith(node_name_prefix)), None)
        if not node:
            return

        identifier = get_node_index_or_identifier_by("index", "name", "inputs", node, property_name, debug=False)
        if identifier is None:
            return        
        
        new_value = property_values[getattr(self, call_from)]
        node.inputs[identifier].default_value = new_value
    

    vec_color_shading: EnumProperty(
        name="Vector Color / Shading",
        description="Show Shading or Velocity",
        items=[
            ('SHADING',  "Shading",  "", 0),
            ('VELOCITY', "Velocity", "", 1),
        ],
        default='SHADING',
        update=lambda self, context: self.update_node_property(self, context, "switch_velocity", "Fac", {'SHADING': 0, 'VELOCITY': 1}, "vec_color_shading")
    )

    # Excepcion para la Cycles lava:
    cycles_lava_shading_or_mask: EnumProperty(
        name="Vector Color / Shading",
        description="Show Shading or Mask",
        items=[
            ('SHADING',  "Shading",  "", 0),
            ('VELOCITY', "Mask", "", 1),
        ],
        default='SHADING',
        update=lambda self, context: self.update_node_property(self, context, "switch_velocity", "Fac", {'SHADING': 0, 'VELOCITY': 1}, "cycles_lava_shading_or_mask")
    )

    uv_vector_switch: EnumProperty(
        name="UVs Generated / Object",
        description="Use UVs in Generated or Object",
        items=[
            ('GENERATED',   "Generated",    "", 0),
            ('OBJECT',      "Object",       "", 1),
        ],
        default='OBJECT',
        update=lambda self, context: self.update_node_property(self, context, "UV_Vector_Switch", "Factor", {'GENERATED': 0, 'OBJECT': 1}, "uv_vector_switch")
    )

    blackbody_switcher: EnumProperty(
        name="Normal / Velocity",
        description="Show Normal Temperature or Temperature with Velocity",
        items=[
            ('NORMAL',  "Normal",  "", 0),
            ('VELOCITY', "Velocity", "", 1),
        ],
        default='NORMAL',
        update=lambda self, context: self.update_node_property(self, context, "CL_Blackbody_Switch", "Factor", {'NORMAL': 0, 'VELOCITY': 1}, "blackbody_switcher")
    )

    pointiness_mask_or_color: EnumProperty(
        name="Pointiness Mask / Color",
        description="Show Mask or Color",
        items=[
            ('COLOR',   "Color",    "", 0),
            ('MASK',    "Mask",     "", 1),
        ],
        default='COLOR',
        update=lambda self, context: self.update_node_property(self, context, "ViewPointinessMask", "Factor", {'COLOR': 1, 'MASK': 0}, "pointiness_mask_or_color")
    )

    def get_default_properties(self, target_prop):
        for prop in self.bl_rna.properties:
            if prop.identifier == target_prop:
                if hasattr(prop, "default"):
                    return prop.default
    